/*********************************************************************************
 *      Copyright:  (C) HuangXiangming
 *                  All rights reserved.
 *
 *       Filename:  socket_reconnect.c
 *    Description:  This file is connect-related function
 *                 
 *        Version:  1.0.0(25/04/03)
 *         Author:  HuangXiangming <huangxiangming@gmail.com>
 *      ChangeLog:  1, Release initial version on "25/04/03 18:18:21"
 *                 
 ********************************************************************************/
#include <netdb.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>      //socket、connect
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h> 
#include <string.h>     //strerror
#include <arpa/inet.h>

#include "socket.h"
#include "logger.h"

//socket初始化
int socket_init(socket_t *sock, char *hostname, int port)
{
	if ( !hostname || !port )
	{
		return 0;
	}

	sock->fd = -1;
	sock->port = port;
	strcpy(sock->hostname, hostname);
	sock->connected = 0;

	return 1;
}

//连接
int socket_connect(socket_t *sock)
{        
	struct sockaddr_in      	servaddr;     
	int                     	rv = -1; 

	sock->fd = socket(AF_INET, SOCK_STREAM, 0);     //创建文字描述符
	if ( sock->fd < 0 ) 
	{    
		return -1;      
	}   
	log_debug("create socket fd[%d] successfully\n", sock->fd);
					
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(sock->port);  

	if ( inet_aton(sock->hostname, &servaddr.sin_addr) == 0 )
	{
		log_debug("Input is not a valid IPv4 address. Attempting to resolve as a domain name...\n");

		struct addrinfo 		hints, *res, *p;		//初始化hints结构体
		memset(&hints, 0, sizeof(hints));
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM; 		// 流式套接字（TCP）
		hints.ai_protocol = IPPROTO_TCP;

		int status = getaddrinfo(sock->hostname, NULL, &hints, &res);
		if (status != 0) 
		{
			log_error("Domain name resolution failed.\n");
			return -1;
		}
		for ( p = res; p != NULL; p = p->ai_next )
		{
			struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
			servaddr.sin_addr = ipv4->sin_addr;

			log_debug("start connect to server [%s:%d]\n", sock->hostname, sock->port);
			rv=connect(sock->fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
			if ( rv < 0 )
			{   
				log_debug("connect to server failed\n");
				continue;
			}
			else
			{   
				sock->connected = 1;
				log_info("connect to server[%s:%d] successfully!\n", sock->hostname, sock->port);
				freeaddrinfo(res);  // 释放资源
				return 0;
			}

		}
		freeaddrinfo(res);  // 释放资源
		log_debug("Domain name resolution failed\n", sock->hostname, sock->port);
		return -1;
	}

	log_debug("start connect to server [%s:%d]\n", sock->hostname, sock->port);
	rv=connect(sock->fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
	if ( rv < 0 ) 
	{   
		log_debug("connect to server failed\n");
		socket_close(sock);
		return -1;
	}
	else
	{
		sock->connected = 1;
		log_info("connect to server[%s:%d] successfully!\n", sock->hostname, sock->port);
	}

	return 0;
}

//关闭fd
int socket_close(socket_t *sock)
{
	if( !sock )		//sock为空(避免段错误)
	{
		log_error("Invalid input arguments\n");
		return 0;
	}

	if( sock->fd < 0 )
	{
		sock->connected = 0;
		return 0;
	}

	close(sock->fd);
	sock->fd = -1;
	sock->connected = 0;

	return 0;
}

//检查fd
int socket_diag(socket_t *sock)
{
	int         	error;
	socklen_t       len_error = sizeof(error);
	char			buf[1024];

	if( !sock )		//sock为空
	{
		return 0;
	}

	if( sock->fd< 0 || !sock->connected )	//断开
	{
		return 0;
	}

	//判断连接状态、服务器连接
	if ( getsockopt(sock->fd, SOL_SOCKET, SO_ERROR, &error, &len_error) < 0 )
	{
		log_debug("getsockopt failure\n");
		socket_close(sock);
		return 0;
	}

	if (error != 0)
	{
		log_debug("connect failed: %s\n", strerror(errno));
		socket_close(sock);
		return 0;
	}

	/*  if ( recv(sock->fd, buf, sizeof(buf), MSG_PEEK | MSG_DONTWAIT) == 0 )
	{
		log_debug("connect failed: %s\n", strerror(errno));
		socket_close(sock);
		return 0;
	}	*/

	return 1;
}

//读
int socket_read(socket_t *sock)
{
	int				rv = -1;
	char			buf[1024];

	if( !sock )     //sock为空
	{           
		return -1;
	}       
	             
	if( sock->fd< 0 || !sock->connected )   //断开
	{
		return -1;
	}   

	memset(buf, 0, sizeof(buf));
	rv=read(sock->fd, buf, sizeof(buf));
	if ( rv < 0 )
	{
		log_warn("Read data from server by fd[%d] failure: %s\n", sock->fd, strerror(errno));
		socket_close(sock);
		return -1;
	}
	else if ( rv == 0 )
	{
		log_warn("Socket[%d] get disconnected\n", sock->fd);
		socket_close(sock);
		return -1;
	}

	if ( !sizeof(buf) )
	{
		return -1;
	}

	log_info("Read %d bytes data from Server: %s\n", rv, buf);

	return 0;
}

int socket_write(socket_t *sock, char *data, int len)
{
	int			rv = -1;
	int			left_bytes = len;
	int			i = 0;

	if( !sock || !data || len <= 0 )     //sock为空
	{           
		return -1;
	}       
	           
	if( sock->fd< 0 || !sock->connected )   //断开
	{
		return -1;
	}   

	log_debug("start write to server");

	while( left_bytes > 0 )
	{
		rv = write(sock->fd, &data[i], left_bytes);
		if ( rv < 0 )
		{   
			log_warn("write to server by fd[%d] failure: %s\n", sock->fd, strerror(errno));
			socket_close(sock);
			return -1;
		}
		else if ( rv == 0 )
		{
			log_debug("sent over\n");
			return 0;
		}

		i += rv;
		left_bytes -= rv;
		continue;
	}

	return 0;
}


